<!DOCTYPE html>
<html lang="zh_cn">
<head>
          <title>来玩魔王的咚</title>
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <meta charset="utf-8" />
        <!-- twitter card metadata -->
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="/images/mowang.png">
<meta name="twitter:site" content="">
<meta name="twitter:title" content="Ansible Roles和最佳实践">
<meta name="twitter:description" content="<p>Roles目录结构；多平台支持：tasks级别，role级别，playbook级别；Using Roles；角色依赖关系；角色中嵌入模块和插件；项目目录结构</p>">
        <!-- OG Tags -->
<meta property="og:url" content="/ansible-roles-and-best-practices.html"/>
<meta property="og:title" content="来玩魔王的咚 | Ansible Roles和最佳实践" />
<meta property="og:description" content="<p>Roles目录结构；多平台支持：tasks级别，role级别，playbook级别；Using Roles；角色依赖关系；角色中嵌入模块和插件；项目目录结构</p>" />
        <!-- favicon -->
        <link rel="icon" type="image/png" href="/images/mowang.png">
        <!-- moment.js for date formatting -->
        <script src="/theme/js/moment.js"></script>
        <!-- css -->
        <link rel="stylesheet" type="text/css" href="/theme/css/main.css" />
        <!-- 左边的menu，如果页面高度不够，就跟着滚动，否则文章分类显示不全 -->
        <link rel="stylesheet" type="text/css" href="/theme/css/mycss/menu.css" />
		<script>
			
                /*! grunt-grunticon Stylesheet Loader - v2.1.2 | https://github.com/filamentgroup/grunticon | (c) 2015 Scott Jehl, Filament Group, Inc. | MIT license. */
    
    (function(e){function t(t,n,r,o){"use strict";function a(){for(var e,n=0;u.length>n;n++)u[n].href&&u[n].href.indexOf(t)>-1&&(e=!0);e?i.media=r||"all":setTimeout(a)}var i=e.document.createElement("link"),l=n||e.document.getElementsByTagName("script")[0],u=e.document.styleSheets;return i.rel="stylesheet",i.href=t,i.media="only x",i.onload=o||null,l.parentNode.insertBefore(i,l),a(),i}var n=function(r,o){"use strict";if(r&&3===r.length){var a=e.navigator,i=e.Image,l=!(!document.createElementNS||!document.createElementNS("http://www.w3.org/2000/svg","svg").createSVGRect||!document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Image","1.1")||e.opera&&-1===a.userAgent.indexOf("Chrome")||-1!==a.userAgent.indexOf("Series40")),u=new i;u.onerror=function(){n.method="png",n.href=r[2],t(r[2])},u.onload=function(){var e=1===u.width&&1===u.height,a=r[e&&l?0:e?1:2];n.method=e&&l?"svg":e?"datapng":"png",n.href=a,t(a,null,null,o)},u.src="",document.documentElement.className+=" grunticon"}};n.loadCSS=t,e.grunticon=n})(this);(function(e,t){"use strict";var n=t.document,r="grunticon:",o=function(e){if(n.attachEvent?"complete"===n.readyState:"loading"!==n.readyState)e();else{var t=!1;n.addEventListener("readystatechange",function(){t||(t=!0,e())},!1)}},a=function(e){return t.document.querySelector('link[href$="'+e+'"]')},c=function(e){var t,n,o,a,c,i,u={};if(t=e.sheet,!t)return u;n=t.cssRules?t.cssRules:t.rules;for(var l=0;n.length>l;l++)o=n[l].cssText,a=r+n[l].selectorText,c=o.split(");")[0].match(/US\-ASCII\,([^"']+)/),c&&c[1]&&(i=decodeURIComponent(c[1]),u[a]=i);return u},i=function(e){var t,o,a;o="data-grunticon-embed";for(var c in e)if(a=c.slice(r.length),t=n.querySelectorAll(a+"["+o+"]"),t.length)for(var i=0;t.length>i;i++)t[i].innerHTML=e[c],t[i].style.backgroundImage="none",t[i].removeAttribute(o);return t},u=function(t){"svg"===e.method&&o(function(){i(c(a(e.href))),"function"==typeof t&&t()})};e.embedIcons=i,e.getCSS=a,e.getIcons=c,e.ready=o,e.svgLoadedCallback=u,e.embedSVG=u})(grunticon,this);
                
                grunticon(["/theme/css/icons.data.svg.css", "/theme/css/icons.data.png.css", "/theme/css/icons.fallback.css"]);
            </script>
        <noscript><link href="/theme/css/icons.fallback.css" rel="stylesheet"></noscript>
        <!-- menu toggle javascript -->
        <script type="text/javascript">
            document.addEventListener("DOMContentLoaded", initMenu);
            
            function initMenu(){
                var menu = document.getElementById("menu");
                var menulink = document.getElementById("menu-link");
                menulink.addEventListener("click", function toggleMenu(){
                        window.event.preventDefault();
                        menulink.classList.toggle('active');
                        menu.classList.toggle('active');              
                    });
            };
        </script>
        <!-- 不蒜子 -->
        <script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>

    <meta name="description" content="<p>Roles目录结构；多平台支持：tasks级别，role级别，playbook级别；Using Roles；角色依赖关系；角色中嵌入模块和插件；项目目录结构</p>" />

    <meta name="tags" content="ansible" />
  <!-- 替换部分base的样式，看文章时，要再宽一点，右边有很多空间可以撑开 -->
  <link rel="stylesheet" type="text/css" href="/theme/css/mycss/article.css" />

</head>
<body>
    <div role="banner" id="masthead">
        <header>
            <a href="/"><img src="/images/mowang.png" alt="McManus Logo"></a>
                <h1>来玩魔王的咚@骑士救兵</h1>
            <a href="#menu" id="menu-link">more stuff</a>
            <nav id="menu">
                <ul>
                        <li><a href="/tags">tags</a></li>
                            <li><a href="/category/cloud.html">Cloud</a></li>
                            <li><a href="/category/docker.html">Docker</a></li>
                            <li><a href="/category/go.html">Go</a></li>
                            <li><a href="/category/linux.html">Linux</a></li>
                            <li><a href="/category/python.html">Python</a></li>
                            <li><a href="/category/xue-xi-bi-ji.html">学习笔记</a></li>
                            <li class="active"><a href="/category/yun-wei-zi-dong-hua.html">运维自动化</a></li>
                </ul>
            </nav>
        </header>
    </div>
        <div class="page" role="main">
  <div class="article" role="article">
    <article>
        <footer>
            <a name="top"></a>
            <p>
              <time datetime=" 2019-09-09 09:43:51+08:00">
                <script>document.write(moment('2019-09-09 09:43:51+08:00').format('LL'));</script>
              </time>
              ~
              <time datetime=" 2019-09-09 09:43:51+08:00">
                <script>document.write(moment('2021-07-05 10:30:00+08:00').format('LL'));</script>
              </time>
            </p>
        </footer>
        <header>
          <h2>
            Ansible Roles和最佳实践
          </h2>
        </header>
      <div class="content">
         <div class="toc">
<ul>
<li><a href="#roles-mu-lu-jie-gou">Roles 目录结构</a></li>
<li><a href="#duo-ping-tai-zhi-chi">多平台支持</a><ul>
<li><a href="#tasks-duo-ping-tai-zhi-chi">tasks 多平台支持</a></li>
<li><a href="#roles-duo-ping-tai-zhi-chi">roles 多平台支持</a></li>
<li><a href="#group_by-mo-kuai-zi-dong-dui-zhu-ji-fen-zu">group_by 模块自动对主机分组</a></li>
</ul>
</li>
<li><a href="#using-roles">Using Roles</a></li>
<li><a href="#jue-se-yi-lai-guan-xi">角色依赖关系</a></li>
<li><a href="#jue-se-zhong-qian-ru-mo-kuai-he-cha-jian">角色中嵌入模块和插件</a></li>
<li><a href="#xiang-mu-mu-lu-jie-gou">项目目录结构</a></li>
</ul>
</div>
<p>角色(roles)：把playbook根据功能，如handler，tasks等分门别类的放在在各自的子目录下，形成一个集合，就是角色。<br>
Roles目录可以是ansible.cfg中roles_path定义的路径，也可以和入口Playbook文件存放在同级目录。建议使用roles_path，方便统一管理。这篇的示例使用和入口Playbook文件存放在同级目录。  </p>
<blockquote>
<p>Roles are ways of automatically loading certain vars_files, tasks, and handlers based on a known file structure. Grouping content by roles also allows easy sharing of roles with other users.  </p>
</blockquote>
<p>Roles的官方文档的连接：<br>
<a href="https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse_roles.html">https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse_roles.html</a>  </p>
<p>github上官方的简单示例：<br>
<a href="https://github.com/ansible/ansible-examples">https://github.com/ansible/ansible-examples</a>  </p>
<p>最佳实践的官方文档：<br>
<a href="https://docs.ansible.com/ansible/latest/user_guide/playbooks_best_practices.html">https://docs.ansible.com/ansible/latest/user_guide/playbooks_best_practices.html</a>  </p>
<h3 id="roles-mu-lu-jie-gou"><a class="toclink" href="#roles-mu-lu-jie-gou">Roles 目录结构</a></h3>
<p>Roles依赖目录命名规则和目录摆放。下面是官方的示例，定义了一个Roles的目录结构：</p>
<div class="highlight"><pre><span></span><code>site.yml
webservers.yml
fooservers.yml
roles/
    common/
        tasks/
        handlers/
        files/
        templates/
        vars/
        defaults/
        meta/
    webservers/
        tasks/
        defaults/
        meta/
</code></pre></div>

<ul>
<li>*.yml文件: 入口Playbook文件</li>
<li>site.yml: 这个名字一般就是部署项目的Playbook文件名。如果还有其他任务在创建Playbook，名字随意。</li>
<li>roles目录: 存放Roles的目录。下面每一个子目录就是一个角色，这里有common和webservers</li>
</ul>
<p>在每个角色下，根据功能按需创建目录，目录名有规范。每个目录下都要有一个main.yml文件，ansible会调用这个文件。<br>
目录名称的含义如下：</p>
<ul>
<li>tasks: 定义角色的任务列表，可以使用include包含其它的位于此目录的task文件</li>
<li>handlers: 定义角色中触发条件时执行的动作</li>
<li>defaults: 设定默认变量，优先级极低，基本就是该变量没有值的时候才会用到这个值</li>
<li>vars: 定义角色使用的变量，优先级高</li>
<li>files: 用来存放由copy模块或script模块调用的文件。不需要main.yml文件</li>
<li>templates: 用来存放jinjia2模板，template模块会自动在此目录中寻找jinjia2模板文件。不需要main.yml文件</li>
<li>meta: 定义角色的元数据，主要是用作依赖关系</li>
</ul>
<h3 id="duo-ping-tai-zhi-chi"><a class="toclink" href="#duo-ping-tai-zhi-chi">多平台支持</a></h3>
<p>官方有一个在tasks里区分的多平台支持的示例。<br>
另外还有一个从书上看到的不同平台定义不同角色名的示例。<br>
最后是一个在官方最佳实践中的示例。  </p>
<h4 id="tasks-duo-ping-tai-zhi-chi"><a class="toclink" href="#tasks-duo-ping-tai-zhi-chi">tasks 多平台支持</a></h4>
<p>tasks级别，对应tasks目录。<br>
定义好角色，角色下定义多个task文件。在main.yml中进行条件判断，导入对应的task文件。<br>
tasks/main.yml是必须有的，作为tasks的入口文件，但是实际的代码不用写在这个文件里，可以在这个文件里包含其他yaml文件。<br>
官方有一个多平台支持的示例：</p>
<div class="highlight"><pre><span></span><code><span class="c1"># roles/example/tasks/main.yml</span><span class="w"></span>
<span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">import_tasks</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">redhat.yml</span><span class="w"></span>
<span class="w">  </span><span class="nt">when</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">ansible_facts[&#39;os_family&#39;]|lower == &#39;redhat&#39;</span><span class="w"></span>
<span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">import_tasks</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">debian.yml</span><span class="w"></span>
<span class="w">  </span><span class="nt">when</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">ansible_facts[&#39;os_family&#39;]|lower == &#39;debian&#39;</span><span class="w"></span>

<span class="c1"># roles/example/tasks/redhat.yml</span><span class="w"></span>
<span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">yum</span><span class="p">:</span><span class="w"></span>
<span class="w">    </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="s">&quot;httpd&quot;</span><span class="w"></span>
<span class="w">    </span><span class="nt">state</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">present</span><span class="w"></span>

<span class="c1"># roles/example/tasks/debian.yml</span><span class="w"></span>
<span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">apt</span><span class="p">:</span><span class="w"></span>
<span class="w">    </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="s">&quot;apache2&quot;</span><span class="w"></span>
<span class="w">    </span><span class="nt">state</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">present</span><span class="w"></span>
</code></pre></div>

<h4 id="roles-duo-ping-tai-zhi-chi"><a class="toclink" href="#roles-duo-ping-tai-zhi-chi">roles 多平台支持</a></h4>
<p>角色级别，对应roles下的角色子目录，比如common和webservers目录。<br>
直接定义好多个角色，有条件地将所有的角色导入。只有满足要求的角色会被导入执行。<br>
在roles层面来支持多平台。预先为不同平台定义好不用的角色。比如两个角色名分别为 httpd_db 和 httpd_rh。那么Playbook文件可以这么写：</p>
<div class="highlight"><pre><span></span><code><span class="c1"># site.yml</span><span class="w"></span>
<span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">install httpd</span><span class="w"></span>
<span class="w">  </span><span class="nt">hosts</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">webservers</span><span class="w"></span>
<span class="w">  </span><span class="nt">roles</span><span class="p">:</span><span class="w"></span>
<span class="w">    </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="p p-Indicator">{</span><span class="nt"> role</span><span class="p">:</span><span class="w"> </span><span class="nv">httpd_db</span><span class="p p-Indicator">,</span><span class="nt"> when</span><span class="p">:</span><span class="w"> </span><span class="nv">ansible_os_family == &#39;Deian&#39;</span><span class="w"> </span><span class="p p-Indicator">}</span><span class="w"></span>
<span class="w">    </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="p p-Indicator">{</span><span class="nt"> role</span><span class="p">:</span><span class="w"> </span><span class="nv">httpd_rh</span><span class="p p-Indicator">,</span><span class="nt"> when</span><span class="p">:</span><span class="w"> </span><span class="nv">ansible_os_family == &#39;RedHat&#39;</span><span class="w"> </span><span class="p p-Indicator">}</span><span class="w"></span>
</code></pre></div>

<p>这里的判断平台的方式和上面的应该是一样的，都是通过Ansible自动获取的主机信息来进行自动判断。这里的写法比上面的官网的写法low。我没有实际测试，就保留原来的写法了。  </p>
<h4 id="group_by-mo-kuai-zi-dong-dui-zhu-ji-fen-zu"><a class="toclink" href="#group_by-mo-kuai-zi-dong-dui-zhu-ji-fen-zu">group_by 模块自动对主机分组</a></h4>
<p>playbook级别，对应roles的同级目录，比如site.yml文件。<br>
先执行一个 group_by 模块的task，对主机进行动态分组。之后再分别对组进行playbook的设置。<br>
这部分内容和roles无关，用了roles可以套用，没用roles也可以套用。是官方最佳实践里的做法。<br>
group_by 模块可以根据关键字对主机进行分组。关键字可以从主机信息中提取，提取到之后，可以先拼接上一段自定义的前缀或后缀。最终获得的字符串会成为一个主机组的组名，这样Playbook中的hosts就可以直接填写这个组名：</p>
<div class="highlight"><pre><span></span><code><span class="nn">---</span><span class="w"></span>
<span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">talk to all hosts just so we can learn about them</span><span class="w"></span>
<span class="w">  </span><span class="nt">hosts</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">all</span><span class="w"></span>
<span class="w">  </span><span class="nt">tasks</span><span class="p">:</span><span class="w"></span>
<span class="w">    </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">Classify hosts depending on their OS distribution</span><span class="w"></span>
<span class="w">      </span><span class="nt">group_by</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="nt">key</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">os_{{ ansible_facts[&#39;distribution&#39;] }}</span><span class="w"></span>

<span class="c1"># now just on the CentOS hosts...</span><span class="w"></span>
<span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">hosts</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">os_CentOS</span><span class="w"></span>
<span class="w">  </span><span class="nt">gather_facts</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">False</span><span class="w"></span>
<span class="w">  </span><span class="nt">tasks</span><span class="p">:</span><span class="w"></span>
<span class="w">    </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="c1"># tasks that only happen on CentOS go here</span><span class="w"></span>
</code></pre></div>

<p>通过 group_by 模块动态为主机添加了分组，还可以事先创建好组变量文件，这些组变量也是会生效的。  </p>
<h3 id="using-roles"><a class="toclink" href="#using-roles">Using Roles</a></h3>
<p>官网有各种花式技巧：<br>
<a href="https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse_roles.html#using-roles">https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse_roles.html#using-roles</a>  </p>
<p>各种花式技巧：</p>
<ul>
<li>把role写在tasks中，这是一种新的语法格式。可以在引入role的前后执行别的task</li>
<li>使用绝对路径引入role</li>
<li>引入role的同时，定义变量</li>
<li>有条件的导入role，上面roles的多平台就是这么用的</li>
<li>为角色添加标签。这样就可以通过tag决定是否要执行或者跳过这个角色的执行</li>
</ul>
<h3 id="jue-se-yi-lai-guan-xi"><a class="toclink" href="#jue-se-yi-lai-guan-xi">角色依赖关系</a></h3>
<p>通过角色依赖，可以在使用角色时自动加入其它角色。角色依赖定义在 meta/main.yml 文件中的 dependencies 下，内容包含当前角色的依赖角色和依赖角色的参数列表。<br>
配置文件示例：</p>
<div class="highlight"><pre><span></span><code><span class="nn">---</span><span class="w"></span>
<span class="nt">dependencies</span><span class="p">:</span><span class="w"></span>
<span class="w">  </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">role</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">common</span><span class="w"></span>
<span class="w">    </span><span class="nt">vars</span><span class="p">:</span><span class="w"></span>
<span class="w">      </span><span class="nt">some_parameter</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">3</span><span class="w"></span>
<span class="w">  </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">role</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">apache</span><span class="w"></span>
<span class="w">    </span><span class="nt">vars</span><span class="p">:</span><span class="w"></span>
<span class="w">      </span><span class="nt">apache_port</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">80</span><span class="w"></span>
<span class="w">  </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">role</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">postgres</span><span class="w"></span>
<span class="w">    </span><span class="nt">vars</span><span class="p">:</span><span class="w"></span>
<span class="w">      </span><span class="nt">dbname</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">blarg</span><span class="w"></span>
<span class="w">      </span><span class="nt">other_parameter</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">12</span><span class="w"></span>
</code></pre></div>

<p>角色的依赖关系，仅仅是meta文件中的一个key，也就是dependencies。别的key还有allow_duplicates，这个是定义角色可以重复执行任务的。其他key暂时在文档中没有出现。  </p>
<h3 id="jue-se-zhong-qian-ru-mo-kuai-he-cha-jian"><a class="toclink" href="#jue-se-zhong-qian-ru-mo-kuai-he-cha-jian">角色中嵌入模块和插件</a></h3>
<p>在目录结构中，列出了角色使用的基本目录名称。这里的自定义模块和插件也是放在roles目录下的角色名子目录中的。<br>
新的目录结构：</p>
<div class="highlight"><pre><span></span><code>roles/
    webservers/
        tasks/
        defaults/
        meta/
        library/
            module1
            module2
        filter_plugins
            filter1
            filter2
</code></pre></div>

<p>在webservers角色下添加了自定义模块，目录名称使用library。<br>
ansible的插件有很多种，这里是在webserver角色下，为filter插件添加了两个自定义功能的文件。  </p>
<h3 id="xiang-mu-mu-lu-jie-gou"><a class="toclink" href="#xiang-mu-mu-lu-jie-gou">项目目录结构</a></h3>
<p>这里是 Ansible 官方最佳实践中推荐的一个工作目录的结构：</p>
<div class="highlight"><pre><span></span><code>production                # 生产环境的 inventory 文件
stage                     # 试运行环境的 inventory 文件

group_vars/               # 定义组变量
   group1
   group2
host_vars/                # 定义主机变量
   hostname1
   hostname2

library/                  # 如果有自定义的模块，放在这里(可选)
module_utils/             # 如果有自定义的模块中要使用的工具，放在这里(可选)
filter_plugins/           # 如果有自定义的过滤插件，放在这里(可选)

site.yml                  # 主 playbook
webservers.yml            # Web 服务器的 playbook
dbservers.yml             # 数据库服务器的 playbook

roles/                    # role 文件存放目录
    common/               # common 角色目录
        tasks/            #
            main.yml      #  task 入口
        handlers/         #
            main.yml      #  handler 入口
        templates/        #  存放模板文件
            ntp.conf.j2   #
        files/            #  存放文件资源
            bar.txt       #
            foo.sh        #
        vars/             #
            main.yml      #  定义角色使用的变量
        defaults/         #
            main.yml      #  定义角色默认变量
        meta/             #
            main.yml      #  定义角色依赖

    webservers/           # webservers 角色目录，不展开了
</code></pre></div>

<p>这里的自定义模块和插件是放在项目目录下的，不会自动生效。<br>
library的默认值: <code>~/.ansible/plugins/modules:/usr/share/ansible/plugins/modules</code><br>
filter_plugins的默认值: <code>~/.ansible/plugins/filter:/usr/share/ansible/plugins/filter</code><br>
官方文档的这篇里可以查到很多有关Ansible设置相关的信息，包括每个变量在配置文件中的名称，在环境变量中的名称，以及变量的默认值。<br>
<a href="https://docs.ansible.com/ansible/latest/reference_appendices/config.html">https://docs.ansible.com/ansible/latest/reference_appendices/config.html</a>  </p>
<p>可以去修改 ansible.cfg 配置文件，也可以利用默认的家目录中的位置，创建软连接。  </p>
      </div>
      <div class="back-to-top">
        <a href="/">HOME</a>
        <a href="#top">TOP</a>
      </div>
    </article>
  </div>
<!-- end article -->
<!-- 页面往下滚动一段之后才会显示TOC -->
<script>
  window.onscroll = function() {
    var tocbox = document.getElementsByClassName('toc')[0];
    var osTop = document.documentElement.scrollTop || document.body.scrollTop;
    var osWidth = document.documentElement.scrollWidth || document.body.scrollWidth;
    // console.log(osTop)
    if (osTop>300 && osWidth>865) {
      tocbox.style.display = "block"
    }
    if (osTop<300 || osWidth<865) {
      tocbox.style.display = "none"
    }
  }
</script>
                <footer>
                    <div class="icons">
                    </div>
                    <span id="busuanzi_container_page_pv" style="padding: 10px">本文阅读量<span id="busuanzi_value_page_pv"></span>次</span>
                    <span id="busuanzi_container_site_pv" style="padding: 10px">本站总访问量<span id="busuanzi_value_site_pv"></span>次</span>
                    <span id="busuanzi_container_site_uv" style="padding: 10px">本站总访客数<span id="busuanzi_value_site_uv"></span>人</span>
                    <p>© <script>document.write(moment().format('YYYY'));</script> 749B</p>
                </footer>
        </div>
</body>
</html>